home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung CD 2 (Tewi)(1994).iso
/
doc
/
ems
/
disk2
/
rolodex.c
< prev
next >
Wrap
Text File
|
1989-11-20
|
47KB
|
990 lines
/**********************************************************************/
/* */
/* Module: R O L O D E X */
/* */
/* */
/* The intent of this example program is to show how to store */
/* data in expanded memory using the C Memory Manager -- MEMLIB. */
/* This program simulates a rolodex using a doubly-linked-list */
/* data structure. The user can insert, delete, edit, and save */
/* the rolodex data to a file. This example shows what */
/* developers need to do in order to manipulate expanded memory */
/* in their own application by using MEMLIB routines. */
/* */
/* If you take a look at the NODE data structure, you'll */
/* notice that there aren't any pointers as you would */
/* traditionally see in such a structure. We use token */
/* identifiers for the blocks of expanded memory they're */
/* associated with. Since we'll be mapping these blocks in and */
/* out of expanded memory, the tokens will be exactly what we */
/* need to keep track of them. */
/* */
/**********************************************************************/
/**********************************************************************/
/* Rolodex.h contains all of the other headers, constants, and global */
/* variables needed for this program. Rolodex.h also contains */
/* housekeeping functions for the rolodex: redrawing the screen, */
/* menu choices, cursor control, etc. */
/**********************************************************************/
#include "rolodex.h"
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: void abort (status) */
/* unsigned int status; */
/* */
/* Description: */
/* Aborts the program with an error message. This function */
/* makes use of MEMLIB function 12 -- effreeall(). effreeall() */
/* frees all pages, blocks, and handles associated with this */
/* application. It is imperative that you release all the */
/* expanded memory you've allocated before exiting, so that other */
/* applications can use this memory. If you don't, you'll have */
/* to reboot to recover the lost memory. Notice also that a call */
/* to effreeall() checks the EMM status. In case that call */
/* fails, you'll get an error condtion back to let you know the */
/* memory was not freed. */
/* */
/* Parameters: */
/* input status The error condition of EMM */
/* */
/* Results returned: None */
/* */
/* Calls: effreeall() */
/* */
/* Called by: edit_fields() */
/* main_menu() */
/* main() */
/* */
/* Globals referenced/modified: None */
/* */
/**********************************************************************/
void abort (status)
unsigned int status;
{
unsigned int free_status;
/***********************************************************/
/* Print the error and free all allocated expanded memory. */
/***********************************************************/
printf ("ERROR ERROR ERROR %X \n",status);
free_status = effreeall();
/******************************************************/
/* Make sure we didn't get an error from effreeall(). */
/******************************************************/
if (free_status != PASSED)
printf ("ERROR %X from effreeall()\n");
exit (1);
} /** end abort **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int initialize_list (void) */
/* */
/* Description: */
/* This is just the initialization process for the rolodex */
/* program. It allocates space from expanded memory for the */
/* first rolodex entry -- the logo screen. This is actually the */
/* "dummy" node for the doubley-linked-list data structure that */
/* holds all the rolodex information. If you take a look at the */
/* NODE data structure, you'll notice we don't use traditional */
/* pointers in the structure. We use token identifiers to keep */
/* track of the blocks mapped in and out of expanded memory. */
/* We then map this "dummy" node in, set its "pointers," and read */
/* in the data file "rolodex.dat." As we read in the rolodex */
/* data for each "card," expanded memory is allocated, mapped in, */
/* and the link-list is updated. See insert_node() for more */
/* information on allocating and mapping. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: efmalloc() */
/* set1eptr() */
/* */
/* Called by: main() */
/* */
/* Globals referenced/modified: current_view_node */
/* size_table */
/* offset_table */
/* start_of_list */
/* */
/**********************************************************************/
unsigned int initialize_list (void)
{
FILE *fp; /* file handle */
int prev_node; /* the node before the current node */
int this_node; /* the current node */
int i, j; /* loops */
NODE far *prev_ptr; /* pointer to the previous node */
NODE far *this_ptr; /* pointer to this node */
char buff[TOTAL_SIZE + 1]; /* buffer to read from file */
unsigned int status; /* error tracking */
/**********************************************************/
/* Set up offset table (offsets into NODE data structure) */
/**********************************************************/
offset_table[0] = 0;
for (i = 1; i < NUMBER_OF_OPTIONS; i++)
offset_table[i] = offset_table[i-1] + size_table[i-1];
/*******************************************************************/
/* Allocate a dummy node for the start of the list (start_of_list).*/
/* Rolodex will display a logo when it views this node. */
/*******************************************************************/
status = efmalloc (sizeof(NODE), &start_of_list);
/*********************************/
/* Get access to this dummy node */
/*********************************/
if (status == PASSED)
status = set1eptr (start_of_list, &prev_ptr);
/****************************************************************/
/* Set the pointers on this node to point to the node itself. */
/* This list is a circular-linked list. */
/****************************************************************/
if (status == PASSED)
{
prev_ptr->next_node = start_of_list;
prev_ptr->prev_node = start_of_list;
/****************************/
/* Prepare for reading loop */
/****************************/
prev_node = start_of_list;
current_view_node = start_of_list;
/********************************/
/* Try to open file for reading */
/********************************/
fp = fopen ("rolodex.dat", "r");
if (fp != NULL)
{
/*********************************************/
/* While a full data buffer has been read... */
/*********************************************/
while ((fgets (buff, TOTAL_SIZE + 1, fp) != NULL)
&& (status == PASSED))
{
if (strlen (buff) >= TOTAL_SIZE)
{
/********************************************/
/* Allocate space for this node and set the */
/* previous node to point to it. */
/********************************************/
status = efmalloc (sizeof(NODE), &this_node);
prev_ptr->next_node = this_node;
/***************************/
/* Get access to this node */
/***************************/
if (status == PASSED)
status = set1eptr (this_node, &this_ptr);
/******************************************************/
/* Copy buffer into node, set pointers on the node, */
/* and update variables for stepping through the list.*/
/******************************************************/
if (status == PASSED)
{
for (i = 0; i < TOTAL_SIZE; i++)
this_ptr->data[i] = buff[i];
this_ptr->prev_node = prev_node;
this_ptr->next_node = start_of_list;
prev_node = this_node;
prev_ptr = this_ptr;
}
} /** end if fgets(...) **/
}
/************************************************************/
/* Connect the start to the end. (This is a circular list.)*/
/************************************************************/
status = set1eptr (start_of_list, &this_ptr);
if (status == PASSED)
this_ptr->prev_node = prev_node;
fclose (fp);
} /** end if fp != NULL **/
}
return (status);
}
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int save_list (void) */
/* */
/* Description: */
/* Saves the current contents of the list to a file called */
/* "rolodex.dat". As save_list goes through the list, it gains */
/* access to each node we want to save by mapping in the block */
/* associated with the node. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: set1eptr() */
/* */
/* Called by: main_menu() */
/* */
/* Globals referenced/modified: start_of_list */
/* */
/**********************************************************************/
unsigned int save_list (void)
{
FILE *fp; /* file handle */
int this_node; /* current node to save */
int i; /* looping */
NODE far *this_ptr; /* pointer to current node */
char buff[TOTAL_SIZE + 1]; /* local buffer to write to file */
unsigned int status; /* error tracking */
/*******************************/
/* Get access to start of list */
/*******************************/
status = set1eptr (start_of_list, &this_ptr);
/*************************************************************************/
/* Attempt to open file; if attempt is unsuccessful, set status to failed*/
/*************************************************************************/
fp = fopen("rolodex.dat", "w");
if ((status == PASSED) && (fp != NULL))
{
/*********************************************************/
/* set current node to first node AFTER dummy start node */
/*********************************************************/
this_node = this_ptr->next_node;
/**************************************************************/
/* While we haven't circled all the way back to the start ... */
/**************************************************************/
while ((this_node != start_of_list) && (status == PASSED))
{
/***************************/
/* Get access to this node */
/***************************/
status = set1eptr (this_node, &this_ptr);
if (status == PASSED)
{
/**************************************************/
/* Set local buffer, write it to file, and update */
/* current node to step thru list. */
/**************************************************/
for (i = 0; i < TOTAL_SIZE; i++)
buff[i] = this_ptr->data[i];
buff[TOTAL_SIZE] = '\0';
fprintf (fp, "%s\n", buff);
this_node = this_ptr->next_node;
}
} /** end while **/
fclose (fp);
} /** end if status... **/
return (status);
} /** end save_list **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int display_total_exp_mem (void) */
/* */
/* Description: */
/* Displays the total amount of free expanded memory on the */
/* rolodex main screen. We just call MEMLIB function 1 -- */
/* ememavl() to get the total amount of free expanded memory */
/* available. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: ememavl() */
/* */
/* Called by: show_node() */
/* */
/* Globals referenced/modified: None */
/* */
/**********************************************************************/
unsigned int display_total_exp_mem()
{
unsigned int status;
unsigned long size;
/***************************************************************/
/* Get the amount of expanded memory available and display it. */
/***************************************************************/
status = ememavl (&size);
if (status == PASSED)
{
_settextposition (TOTAL_EXP_MEM_ROW, TOTAL_EXP_MEM_COL);
printf ("Expanded Memory Available: %8lu ", size);
}
return (status);
}
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int display_max_block (void) */
/* */
/* Description: */
/* Displays the largest allocatable block on the main rolodex */
/* screen. We just call MEMLIB function 2 -- ememmax() to get */
/* the largest block available. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: ememmax() */
/* */
/* Called by: show_node() */
/* */
/* Globals referenced/modified: None */
/* */
/**********************************************************************/
unsigned int display_max_block()
{
unsigned int status;
unsigned int size;
/*****************************************************/
/* Get the largest allocatable block and display it. */
/*****************************************************/
status = ememmax (&size);
if (status == PASSED)
{
_settextposition (MAX_BLOCK_ROW, MAX_BLOCK_COL);
printf ("Maximum Block Size: %5u", size);
}
return (status);
}
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int display_block_size (block) */
/* int block; */
/* */
/* Description: */
/* Displays the size (in bytes) of the block of expanded */
/* memory passed to it. (In this context it is the node being */
/* displayed on the screen). We just call MEMLIB function 4 -- */
/* emsize() to get the size of the block by passing the token */
/* identifier of the block we want. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: emsize() */
/* */
/* Called by: show_node() */
/* */
/* Globals referenced/modified: None */
/* */
/**********************************************************************/
unsigned int display_block_size (block)
int block;
{
unsigned int status;
unsigned int size;
/*****************************************************/
/* Get the size of the current block and display it. */
/*****************************************************/
status = emsize (block, &size);
if (status == PASSED)
{
_settextposition (BLOCK_SIZE_ROW, BLOCK_SIZE_COL);
printf ("Size of Node: %5u", size);
}
return (status);
}
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int show_node (node_to_show) */
/* int node_to_show; */
/* */
/* Description: */
/* Draws the requested node on screen; includes all of its */
/* fields and the size of the expanded memory block it's using. */
/* */
/* Parameters: */
/* input node_to_show "pointer" to the node to show */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: set1eptr() */
/* */
/* Called by: edit_fields() */
/* main_menu() */
/* */
/* Globals referenced/modified: option_list */
/* size_table */
/* offset_table */
/* start_of_list */
/* */
/**********************************************************************/
unsigned int show_node (node_to_show)
int node_to_show;
{
int i, j; /* looping */
short row; /* row to output text on */
short col; /* column to output text on */
NODE far *ptr; /* pointer to the node */
char buff[MAX_DATA_SIZE + 1]; /* local buffer for each string */
unsigned int status; /* error tracking */
unsigned int size; /* size of this node */
/**********************************/
/* Get access to the node to show */
/**********************************/
status = set1eptr (node_to_show, &ptr);
if (status == PASSED)
if (node_to_show == start_of_list)
{
/**************************************************/
/* We're at the start of the list - draw the logo */
/**************************************************/
clear_rolodex();
draw_logo();
status = display_total_exp_mem();
if (status == PASSED)
status = display_max_block();
}
else
{
/************************************************/
/* This is real node (NOT the dummy start node) */
/************************************************/
clear_rolodex();
for (i = 0; i < NUMBER_OF_OPTIONS; i++)
{
/*****************************************************/
/* For each option, */
/* - set the row and column */
/* - write the option title to the screen */
/* - copy node data to local buffer */
/* - write node data to the screen */
/*****************************************************/
row = option_list[i].row;
col = option_list[i].column - option_list[i].title_length;
_settextposition(row, col);
_outtext(option_list[i].title);
for (j = 0; j < size_table[i]; j++)
buff[j] = ptr->data[j + offset_table[i]];
buff[j] = '\0';
_outtext(buff);
} /** end for ... **/
status = display_block_size (node_to_show);
} /** end else **/
return (status);
} /** end show_node **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int insert_node (void) */
/* */
/* Description: */
/* Inserts a new node in the linked-list. First, we allocate */
/* the amount of expanded memory required for a node by calling */
/* MEMLIB function 3 -- efmalloc(). We pass the size we want */
/* and get a token identifier for that particuliar block. Each */
/* token is unique to that block of expanded memory. */
/* */
/* Next, we use MEMLIB function 7, set1eptr(), to gain access */
/* to this block (map it in from expanded memory into conventional*/
/* and get a pointer to it). We give set1eptr() the token */
/* identifier of the block we want access to (in this case */
/* "this_node"), and this function does the mapping and returns a */
/* pointer to this block of memory. */
/* */
/* We then allow a user to enter the data for this node by */
/* calling edit_node(). Once the user accepts the data, the new */
/* node is inserted into the sorted-by-last-name linked-list. */
/* list. We use MEMLIB function 8 -- set2eptrs() -- to do this. */
/* This function will map in two blocks and assign them two */
/* separate pointers. This allows access to two blocks at the */
/* same time. Finally, we insert the new node between the two */
/* nodes we just mapped in. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: efmalloc() */
/* set1eptr() */
/* edit_fields() */
/* set2eptrs() */
/* compare_last_names() */
/* */
/* Called by: main_menu() */
/* */
/* Globals referenced/modified: start_of_list */
/* current_view_node */
/* */
/**********************************************************************/
unsigned int insert_node (void)
{
unsigned int status; /* Error tracking */
unsigned int edit_status; /* accept/exit picked from edit_node() */
int this_node; /* "Pointer" to the current node */
int temp_node; /* "Pointer" to node to insert */
int i; /* Loop */
NODE far *this_ptr; /* Pointer to this_node */
NODE far *temp_ptr; /* Pointer to temp_node */
static char *title = /* Title line for edit_fields call */
" INSERT A NEW ENTRY ";
/******************************************************/
/* Allocate space for a new node and get access to it */
/******************************************************/
status = efmalloc (sizeof(NODE), &this_node);
if (status == PASSED)
status = set1eptr (this_node, &this_ptr);
/************************************************************/
/* Set data to '_' for display and then call edit_fields to */
/* insert new data in the node. */
/************************************************************/
if (status == PASSED)
{
for (i = 0; i < TOTAL_SIZE; i++)
this_ptr->data[i] = '_';
edit_status = edit_fields (title, this_node);
}
/************************************************************/
/* If the user chose "accept" (from edit_fields), insert */
/* this node into the list alphabetically. */
/************************************************************/
if ((status == PASSED) && (edit_status == ACCEPT))
{
/***************************************/
/* Get access to the start of the list */
/***************************************/
status = set1eptr (start_of_list, &this_ptr);
/***********************************************************/
/* Get access to the next node in the list (temp_node) and */
/* the node to be inserted (this_node). */
/***********************************************************/
if (status == PASSED)
{
temp_node = this_ptr->next_node;
status = set2eptrs (temp_node, this_node, &temp_ptr, &this_ptr);
}
/***************************************************************/
/* Step through the list as long as we haven't circled back to */
/* the start and the last name of the node to insert is later */
/* in the alphabet than the name of the current node. */
/***************************************************************/
while ((temp_node != start_of_list) &&
(compare_last_names (this_ptr, temp_ptr) > 0) &&
(status == PASSED))
{
/**********************************************************/
/* Step to next node in list and get access to it and the */
/* node we're inserting. */
/**********************************************************/
temp_node = temp_ptr->next_node;
status = set2eptrs (temp_node, this_node, &temp_ptr, &this_ptr);
}
if (status==PASSED)
{
/*********************************************************/
/* temp_node is now pointing at the node we want to */
/* insert in front of. (If we're at the end of the list,*/
/* temp_node points at start_of_list.) Set the pointers */
/* on the new node to point to temp_node and the node */
/* before temp_node. Set the prev_node pointer of temp_ */
/* node to point to the new node. Set the next_node */
/* pointer of the node before temp_node to point to the */
/* new node. */
/*********************************************************/
this_ptr->next_node = temp_node;
this_ptr->prev_node = temp_ptr->prev_node;
temp_ptr->prev_node = this_node;
/* Get access to the previous node */
status = set1eptr (this_ptr->prev_node, &temp_ptr);
temp_ptr->next_node = this_node;
/* Set the current view node to the node we inserted */
current_view_node = this_node;
} /** end if status **/
} /** end if status **/
else
/******************/
/* Free this node */
/******************/
effree (this_node);
return (status);
} /** end insert_node **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int edit_node (void) */
/* */
/* Description: */
/* To edit an existing node. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: efmalloc() */
/* set2eptrs() */
/* edit_fields() */
/* effree() */
/* */
/* Called by: main_menu() */
/* */
/* Globals referenced/modified: start_of_list */
/* current_view_node */
/* */
/**********************************************************************/
unsigned int edit_node (void)
{
unsigned int status; /* Error tracking */
unsigned int edit_status; /* Decide to keep/reject */
int temp_node; /* "Pointer" to the node to edit */
int i; /* Loop */
NODE far *this_ptr; /* pointer to current node */
NODE far *temp_ptr; /* pointer to a temporary node */
static char *title = /* Title line for call to edit_fields */
" EDIT THIS ENTRY ";
status = PASSED;
/****************************************************/
/* Test for editable node (can't edit logo screen!) */
/****************************************************/
if (current_view_node != start_of_list)
{
/**************************************************************/
/* Allocate space for a temporary node (so that changes won't */
/* affect the real node if the user picks "exit.") */
/**************************************************************/
status = efmalloc (sizeof (NODE), &temp_node);
/********************************************************/
/* Get access to the real node and the temporary node. */
/********************************************************/
if (status == PASSED)
status = set2eptrs (temp_node, current_view_node, &temp_ptr, &this_ptr);
/******************************************************/
/* Copy the real node to the temporary one, and call */
/* edit_fields to edit the temporary node. */
/******************************************************/
if (status == PASSED)
{
for (i = 0; i < TOTAL_SIZE; i++)
temp_ptr->data[i] = this_ptr->data[i];
edit_status = edit_fields (title, temp_node);
}
/**************************************************************/
/* Get access to the real and temporary node (edit_fields */
/* called set1eptr, so we no longer have access to both nodes)*/
/**************************************************************/
if ((status == PASSED) & (edit_status == ACCEPT))
status = set2eptrs(temp_node, current_view_node, &temp_ptr, &this_ptr);
/***************************************************************/
/* If the user chose "accept," copy the temporary node to the */
/* real node and free the temporary node. */
/***************************************************************/
if ((status == PASSED) & (edit_status == ACCEPT))
{
for (i = 0; i < TOTAL_SIZE; i++)
this_ptr->data[i] = temp_ptr->data[i];
status = effree (temp_node);
}
} /** end if current_view_node... **/
return(status);
} /** end edit_node **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: unsigned int delete_node (void) */
/* */
/* Description: */
/* Deletes the current view node from the list. After mapping */
/* in the node the user wishes to delete, we map in the nodes */
/* before and after it. We then pass the token of the block to */
/* delete to MEMLIB function 5, effree(), to deallocate it. */
/* */
/* Parameters: None */
/* */
/* Results returned: */
/* Condition of EMM. PASSED or type of error encountered. */
/* */
/* Calls: set1eptr() */
/* set2eptrs() */
/* effree() */
/* */
/* Called by: main_menu() */
/* */
/* Globals referenced/modified: start_of_list */
/* current_view_node */
/* */
/**********************************************************************/
unsigned int delete_node (void)
{
int next_node; /* "Pointer" to the next node in the list */
int prev_node; /* "Pointer" to the previous node */
unsigned int status; /* Error tracking */
NODE far *next_ptr; /* Pointer to next_node */
NODE far *prev_ptr; /* Pointer to previous node */
status = PASSED;
/*********************************************************/
/* Check that we're not trying to delete the logo screen */
/*********************************************************/
if (current_view_node != start_of_list)
{
/**********************************/
/* Get access to the current node */
/**********************************/
status = set1eptr (current_view_node, &next_ptr);
if (status == PASSED)
{
/********************************************************/
/* Get access to the nodes before and after the current */
/* node. */
/********************************************************/
next_node = next_ptr->next_node;
prev_node = next_ptr->prev_node;
status = set2eptrs (next_node, prev_node, &next_ptr, &prev_ptr);
/*************************/
/* Free the current node */
/*************************/
if (status == PASSED)
status = effree (current_view_node);
}
/**********************************************************/
/* Chain the list around the gap left by the current node */
/**********************************************************/
if (status == PASSED)
{
next_ptr->prev_node = prev_node;
prev_ptr->next_node = next_node;
/* Set the current view node to the previous node */
current_view_node = prev_node;
}
} /** end if current_view_node... **/
return (status);
} /** end delete_node **/
/*$PAGE*/
/**********************************************************************/
/* */
/* Name: void main(void) */
/* */
/* Description: */
/* The main body of the program. Initializes the list, calls */
/* main_menu (executive), and frees the nodes when finished. */
/* */
/* Parameters: None */
/* */
/* Results returned: None */
/* */
/* Calls: initialize_list() */
/* draw_border() */
/* main_menu() */
/* effreeall() */
/* abort() */
/* */
/* Called by: None */
/* */
/* Globals referenced/modified: None */
/* */
/**********************************************************************/
void main()
{
unsigned int status;
/* Initialize the data */
status = initialize_list();
if (status != PASSED)
abort(status);
else
{
/* Set up text window and clear it */
_settextwindow (OUTSIDE_START_ROW, OUTSIDE_START_COL, OUTSIDE_END_ROW,
OUTSIDE_END_COL);
_clearscreen (_GCLEARSCREEN);
/* draw the rolodex border */
draw_border();
/* Call main executive routine */
main_menu();
/* Free up nodes used by the rolodex */
status = effreeall();
_clearscreen (_GCLEARSCREEN);
if (status != PASSED)
abort (status);
}
} /** end main **/